﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Minestorm.Evolved.IoC
{
	public class ObjectFactory : IObjectFactory
	{
		private ConstructionMapFactory _constructionMapFactory = new ConstructionMapFactory();
		private TypeMap _typeMap;
		private SingletonCache _singletonCache = new SingletonCache();
		private TypeCreator _typeCreator;

		public ObjectFactory()
		{
			_typeCreator = new TypeCreator(this);
			_typeMap = new TypeMap(_constructionMapFactory, _singletonCache);
		}

		public void ClearFactory(Boolean disposeSingletons)
		{
			if (disposeSingletons)
				_singletonCache.Dispose();

			_singletonCache.Clear();
			_typeMap.Clear();
		}

		public void RegisterMany(Action<IObjectFactory> factory)
		{
			factory(new ObjectFactoryProxy(this));
		}

		public void RegisterConcreteType<I, C>()
			where I : class
			where C : class, I
		{
			_typeMap.AddConcreteTypeMap<I, C>();
		}

		public void RegisterSingleton<I, C>()
			where I : class
			where C : class, I
		{
			_typeMap.AddSingletonTypeMap<I, C>();
		}

        public void RegisterSingleton<I, C>(C instance)
			where I : class
			where C : class, I
		{
			_typeMap.AddSingletonTypeMap<I, C>();
            
			_singletonCache.CacheSingletonInstance<I>(instance);
		}

        public void Dispose()
        {
            _typeMap.Clear();
            _singletonCache.Dispose();
        }

		public I InstanceOf<I>()
			where I : class
		{
			if (!_typeMap.ContainsInterfaceType<I>())
				return null;

			Object instance = _typeCreator.CreateInstanceFromInterfaceType(typeof(I));

			return instance as I;
		}

		public Boolean TypeMapContainsInterfaceType(Type interfaceType)
		{
			return _typeMap.ContainsInterfaceType(interfaceType);
		}

		public ConcreteType GetConcreteTypeFromInterfaceType(Type interfaceType)
		{
			return _typeMap.GetConcreteTypeFor(interfaceType);
		}

		public Object GetSingletonInstance(Type interfaceType)
		{
			Object instance;

			if (!_singletonCache.GetInstance(interfaceType, out instance))
			{
				ConcreteType concreteType = GetConcreteTypeFromInterfaceType(interfaceType);

				if (concreteType == null)
					throw new Exception("");

				instance = _typeCreator.CreateInstanceFromConcreteType(concreteType);

				_singletonCache.CacheSingletonInstance(interfaceType, instance);
			}

			return instance;
		}

		public Boolean IsTypeRegistered<I>()
			where I : class
		{
			return _typeMap.ContainsInterfaceType<I>();
		}
	}
}
